#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import itertools

from common.logger import Logger
from db2.backup.util.dpf_backup_util import DpfBackupUtil
from db2.backup.util.ha_utils import is_power_rhel_ha
from db2.comm.const import Db2Const, Db2JsonConstant
from db2.comm.util.ha_util import HaUtil
from db2.comm.util.param_util import Db2ParamUtil
from db2.comm.util.common_util import Db2CommonUtil
from db2.comm.util.single_util import SingleUtil
from db2.comm.db2_cmd import get_lang_value

LOGGER = Logger().get_logger(filename="db2.log")


def rollforward_tablespace(tablespace_list, os_user, db_name, cache_path, chunk_size=50, encoding=""):
    """分批次前滚表空间"""
    args = [iter(tablespace_list)] * chunk_size
    tablespace_list_splited = list(itertools.zip_longest(*args, fillvalue=None))
    for name_list in tablespace_list_splited:
        filtered_list = [x for x in name_list if x is not None]
        ts_names_str = ",".join(filtered_list)
        Db2CommonUtil.check_injection(ts_names_str)
        rollforward_cmd = f'su - {os_user} -c "db2 rollforward database {db_name} to end of backup and complete ' \
                          f'tablespace \({ts_names_str}\) online overflow log path \({cache_path}\)"'
        SingleUtil.execute_rollforward_ts(rollforward_cmd, encoding)


class SingleRestoreService:
    @staticmethod
    def handle_full_restore_ts(os_user, db_name, res_ts_names, param_dict):
        """表空间集全量副本恢复"""
        cache_path = Db2ParamUtil.get_cache_mount_path_for_restore(param_dict)
        encoding = get_lang_value(os_user)
        # HA性能适配
        resource_type = DpfBackupUtil.get_cluster_type(param_dict)
        if is_power_rhel_ha(resource_type):
            full_copy_path_list = Db2ParamUtil.get_all_full_copy_path_when_restore(param_dict)
            full_copy_path_list = HaUtil.handle_mount_path_for_san(os_user, param_dict, full_copy_path_list)
            full_copy_path = ",".join(full_copy_path_list)
        else:
            full_copy_path = Db2ParamUtil.get_full_copy_path_when_restore(param_dict)

        path = Db2ParamUtil.get_copy_path_info(param_dict)
        full_bak_time = path[0].get("restore_time")
        ts_names_str = ",".join(res_ts_names)
        # 强制断开所有连接
        Db2CommonUtil.disconnect_db_application(os_user, db_name)
        Db2CommonUtil.deactivate_db_status(os_user, db_name)
        Db2CommonUtil.check_os_user_with_ex(os_user)
        Db2CommonUtil.check_injection(db_name, ts_names_str, str(full_bak_time))
        # 恢复表空间集
        full_restore_ts_cmd = f'su - {os_user} -c "db2 restore database {db_name} tablespace \({ts_names_str}\) ' \
                              f'from {full_copy_path} taken at {full_bak_time} ' \
                              f'logtarget {cache_path}"'
        SingleUtil.execute_restore_ts(full_restore_ts_cmd, encoding)
        SingleUtil.check_rollforward_pending_after_exec_restore_ts(os_user, db_name)
        # 前滚表空间
        rollforward_tablespace(res_ts_names, os_user, db_name, cache_path, encoding=encoding)
        # 离线备份表空间集
        off_ts_cmd = f'su - {os_user} -c "db2 backup db {db_name} on all nodes tablespace \({ts_names_str}\) ' \
                     f'to /dev/null"'
        SingleUtil.execute_offline_backup_ts(off_ts_cmd)

    @staticmethod
    def handle_incr_restore_ts(os_user, db_name, res_ts_names, param_dict):
        """表空间集增量副本恢复"""
        cache_path = Db2ParamUtil.get_cache_mount_path_for_restore(param_dict)
        encoding = get_lang_value(os_user)
        resource_type = DpfBackupUtil.get_cluster_type(param_dict)
        if is_power_rhel_ha(resource_type):
            # data_path是一个列表
            path_list = Db2ParamUtil.get_copy_all_path_infos(param_dict)
        else:
            # data_path是一个字符串
            path_list = Db2ParamUtil.get_copy_path_info(param_dict)
        path_list = HaUtil.handle_mount_path_for_san_ts(os_user, param_dict, path_list)
        # 手动恢复顺序：最后一个增量->全量->依次从第一个增量到最后一个增量
        last_manual_restore_copies = path_list[1:]
        manual_restore_copies = [path_list[-1], path_list[0]]
        manual_restore_copies.extend(last_manual_restore_copies)
        ts_names_str = ",".join(res_ts_names)
        # 强制断开所有连接
        Db2CommonUtil.disconnect_db_application(os_user, db_name)
        Db2CommonUtil.deactivate_db_status(os_user, db_name)
        Db2CommonUtil.check_os_user_with_ex(os_user)
        Db2CommonUtil.check_injection(db_name, ts_names_str)
        restore_num = len(manual_restore_copies)
        # 按副本顺序恢复表空间
        for idx, tmp_restore_copy in enumerate(manual_restore_copies):
            # 只在最后一次restore时提取日志
            log_target_cmd = f"logtarget {cache_path} " if idx == (restore_num - 1) else ""
            # HA data_path 返回的是一个列表，其他返回的是一个字符串
            copy_path = ",".join(tmp_restore_copy.get("data_path")) \
                if is_power_rhel_ha(resource_type) \
                else tmp_restore_copy.get("data_path")
            Db2CommonUtil.check_path_list(copy_path)
            tmp_copy_timestamp = tmp_restore_copy.get("restore_time")
            tmp_restore_ts_cmd = f'su - {os_user} -c "db2 restore db {db_name} tablespace ' \
                                 f'\({ts_names_str}\) incremental from {copy_path} ' \
                                 f'taken at {tmp_copy_timestamp} {log_target_cmd}without prompting"'
            SingleUtil.execute_restore_ts(tmp_restore_ts_cmd, encoding)
        # 前滚表空间
        rollforward_tablespace(res_ts_names, os_user, db_name, cache_path, encoding=encoding)
        # 离线备份表空间集
        off_ts_cmd = f'su - {os_user} -c "db2 backup db {db_name} on all nodes tablespace \({ts_names_str}\) ' \
                     f'to /dev/null"'
        SingleUtil.execute_offline_backup_ts(off_ts_cmd)

    @staticmethod
    def handle_diff_restore_ts(os_user, db_name, res_ts_names, param_dict):
        """表空间集差异副本恢复"""
        cache_path = Db2ParamUtil.get_cache_mount_path_for_restore(param_dict)
        encoding = get_lang_value(os_user)
        resource_type = DpfBackupUtil.get_cluster_type(param_dict)
        if is_power_rhel_ha(resource_type):
            # HA data_path是一个列表
            path_list = Db2ParamUtil.get_copy_all_path_infos(param_dict)
        else:
            # data_path是一个字符串
            path_list = Db2ParamUtil.get_copy_path_info(param_dict)
        path_list = HaUtil.handle_mount_path_for_san_ts(os_user, param_dict, path_list)
        # 手动恢复顺序：差异->全量->差异
        manual_restore_copies = [path_list[1], path_list[0], path_list[1]]
        ts_names_str = ",".join(res_ts_names)
        # 强制断开所有连接
        Db2CommonUtil.disconnect_db_application(os_user, db_name)
        Db2CommonUtil.deactivate_db_status(os_user, db_name)
        Db2CommonUtil.check_os_user_with_ex(os_user)
        Db2CommonUtil.check_injection(db_name, ts_names_str)
        # 按副本顺序恢复表空间
        restore_num = len(manual_restore_copies)
        for idx, tmp_restore_copy in enumerate(manual_restore_copies):
            # 只在最后一次restore时提取日志
            log_target_cmd = f"logtarget {cache_path} " if idx == (restore_num - 1) else ""
            # HA data_path 返回的是一个列表，其他返回的是一个字符串
            copy_path = ",".join(tmp_restore_copy.get("data_path")) \
                if is_power_rhel_ha(resource_type) \
                else tmp_restore_copy.get("data_path")
            Db2CommonUtil.check_path_list(copy_path)
            tmp_copy_timestamp = tmp_restore_copy.get("restore_time")
            Db2CommonUtil.check_injection(str(tmp_copy_timestamp))
            tmp_restore_ts_cmd = f'su - {os_user} -c "db2 restore db {db_name} tablespace ' \
                                 f'\({ts_names_str}\) incremental from {copy_path} ' \
                                 f'taken at {tmp_copy_timestamp} {log_target_cmd}without prompting"'
            SingleUtil.execute_restore_ts(tmp_restore_ts_cmd, encoding)
        # 前滚表空间
        rollforward_tablespace(res_ts_names, os_user, db_name, cache_path, encoding=encoding)
        # 离线备份表空间集
        off_ts_cmd = f'su - {os_user} -c "db2 backup db {db_name} on all nodes tablespace \({ts_names_str}\) ' \
                     f'to /dev/null"'
        SingleUtil.execute_offline_backup_ts(off_ts_cmd)
